using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Graphics;

namespace TestGame.HelperClasses
{
    public enum CursorButton
    {
        Left = 0,
        Right = 1,
        Middle = 2,
    }

    /// <summary>
    /// This class modifies a position by the inpout of mouse, keyboard or gampad / zune
    /// </summary>
    public class CursorHelper : IDisposable
    {
        const float Velocity = 250;

#if !ZUNE
        public Keys UpKey = Keys.W;
        public Keys LeftKey = Keys.A;
        public Keys DownKey = Keys.S;
        public Keys RightKey = Keys.D;

        public Keys LeftButtonKey = Keys.Enter;
        public Keys RightButtonKey = Keys.RightShift;
        public Keys MiddleButtonKey = Keys.Space;

#endif
        public Buttons LeftButton = Buttons.A;
        public Buttons RightButton = Buttons.B;
        public Buttons MiddleButton = Buttons.X;


        float _AccurateX, _AccurateY;
        /// <summary>
        /// The current position of the cursor
        /// </summary>
        public Vector2 Position { get; private set; }

        /// <summary>Indicates wether the cursor position is set to the mouse position</summary>
        public bool IsMouseMovementEnabled { get; set; }
        /// <summary>Indicates wether the mouse buttons are checked or not</summary>
        public bool AreMouseButtonsEnabled { get; set; }

        /// <summary>Indicates wether the cursor position can be modified by pressing Keys on the Keyboard</summary>
        public bool IsKeyMovementEnabled { get; set; }
        /// <summary>Indicates wether the Keys of the Keyboards which are mapped to the Cursor Buttons are checked or not</summary>
        public bool AreButtonKeysEnabled { get; set; }

        /// <summary>Indicates wether the cursor position can be modified by using the D-Pad on the Gamepad</summary>
        public bool IsDigitalGamepadMovementEnabled { get; set; }
        /// <summary>Indicates wether the cursor can be modified by usng the left Thumb-Stick of the Gamepad</summary>
        public bool IsAnalogGamepadMovementEnabled { get; set; }
        /// <summary>Indicates wether the Buttons of the Gamepad which are mapped to the Cursor Buttons are checked or not</summary>
        public bool AreGamepadButtonsEnabled { get; set; }

        /// <summary>
        /// Event is called when one of the buttons has just been pushed.
        /// </summary>
        public event Action<CursorButton> ButtonPressed;
        /// <summary>
        /// Event is called when one of the buttons has just been released.
        /// </summary>
        public event Action<CursorButton> ButtonReleased;

        // graphics device manager is for cheking the size of he window
        GraphicsDevice _GraphicsDevice;

        public CursorHelper(GraphicsDevice graphicsDevice)
        {
            _GraphicsDevice = graphicsDevice;

            IsMouseMovementEnabled = true;
            IsKeyMovementEnabled = true;
            IsDigitalGamepadMovementEnabled = true;
            IsAnalogGamepadMovementEnabled = true;

            AreMouseButtonsEnabled = true;
            AreButtonKeysEnabled = true;
            AreGamepadButtonsEnabled = true;
        }

        public void Update(float seconds,
                GamePadState currentButtons, GamePadState previousButtons,
                KeyboardState currentKeys, KeyboardState previousKeys,
                MouseState currentMouse, MouseState previousMouse)
        {
            #region Mouse Input
#if WINDOWS
            if (!IsMouseInWindow(currentMouse))
                return;

            // Mouse Buttons
            if (AreMouseButtonsEnabled)
            {
                // left
                if (currentMouse.LeftButton == ButtonState.Pressed && previousMouse.LeftButton == ButtonState.Released)
                    OnButtonPressed(CursorButton.Left);
                else if (currentMouse.LeftButton == ButtonState.Released && previousMouse.LeftButton == ButtonState.Pressed)
                    OnButtonReleased(CursorButton.Left);
                // right
                if (currentMouse.RightButton == ButtonState.Pressed && previousMouse.RightButton == ButtonState.Released)
                    OnButtonPressed(CursorButton.Right);
                else if (currentMouse.RightButton == ButtonState.Released && previousMouse.RightButton == ButtonState.Pressed)
                    OnButtonReleased(CursorButton.Right);
                // middle
                if (currentMouse.MiddleButton == ButtonState.Pressed && previousMouse.MiddleButton == ButtonState.Released)
                    OnButtonPressed(CursorButton.Middle);
                else if (currentMouse.MiddleButton == ButtonState.Released && previousMouse.MiddleButton == ButtonState.Pressed)
                    OnButtonReleased(CursorButton.Middle);
            }

            // Mouse movement: if mouse has moved, set position and ignore the rest.
            if (IsMouseMovementEnabled)
            {
                if (currentMouse.X != (int)Position.X || currentMouse.Y != (int)Position.Y)
                {
                    Position = new Vector2(currentMouse.X, currentMouse.Y);
                    _AccurateX = currentMouse.X;
                    _AccurateY = currentMouse.Y;
                    return;
                }
            }
#endif
            #endregion

            // calculate speed
            float vel = seconds * Velocity;

            #region Keyboard Input
#if !ZUNE
            // Keyboard buttons
            if (AreButtonKeysEnabled)
            {
                // left
                if (currentKeys.IsKeyDown(LeftButtonKey) && previousKeys.IsKeyUp(LeftButtonKey))
                    OnButtonPressed(CursorButton.Left);
                else if (currentKeys.IsKeyUp(LeftButtonKey) && previousKeys.IsKeyDown(LeftButtonKey))
                    OnButtonReleased(CursorButton.Left);
                // right
                if (currentKeys.IsKeyDown(RightButtonKey) && previousKeys.IsKeyUp(RightButtonKey))
                    OnButtonPressed(CursorButton.Right);
                else if (currentKeys.IsKeyUp(RightButtonKey) && previousKeys.IsKeyDown(RightButtonKey))
                    OnButtonReleased(CursorButton.Right);
                // middle
                if (currentKeys.IsKeyDown(MiddleButtonKey) && previousKeys.IsKeyUp(MiddleButtonKey))
                    OnButtonPressed(CursorButton.Middle);
                else if (currentKeys.IsKeyUp(MiddleButtonKey) && previousKeys.IsKeyDown(MiddleButtonKey))
                    OnButtonReleased(CursorButton.Middle);
            }

            // Keyboard movement
            if (IsKeyMovementEnabled)
            {
                if (currentKeys.IsKeyDown(UpKey))
                    _AccurateY -= vel;
                if (currentKeys.IsKeyDown(DownKey))
                    _AccurateY += vel;
                if (currentKeys.IsKeyDown(LeftKey))
                    _AccurateX -= vel;
                if (currentKeys.IsKeyDown(RightKey))
                    _AccurateX += vel;
            }
#endif
            #endregion

            #region Gamepad / Zune Input
            //Gamepad Buttons
            if (AreGamepadButtonsEnabled)
            {
                // left
                if (currentButtons.IsButtonDown(LeftButton) && previousButtons.IsButtonUp(LeftButton))
                    OnButtonPressed(CursorButton.Left);
                else if (currentButtons.IsButtonUp(LeftButton) && previousButtons.IsButtonDown(LeftButton))
                    OnButtonReleased(CursorButton.Left);
                // right
                if (currentButtons.IsButtonDown(RightButton) && previousButtons.IsButtonUp(RightButton))
                    OnButtonPressed(CursorButton.Right);
                else if (currentButtons.IsButtonUp(RightButton) && previousButtons.IsButtonDown(RightButton))
                    OnButtonReleased(CursorButton.Right);
                // middle
                if (currentButtons.IsButtonDown(MiddleButton) && previousButtons.IsButtonUp(MiddleButton))
                    OnButtonPressed(CursorButton.Middle);
                else if (currentButtons.IsButtonUp(MiddleButton) && previousButtons.IsButtonDown(MiddleButton))
                    OnButtonReleased(CursorButton.Middle);
            }


            //Gamepad Movement
            if (IsDigitalGamepadMovementEnabled)
            {
                // Digital
                if (currentButtons.DPad.Up == ButtonState.Pressed)
                    _AccurateY -= vel;
                if (currentButtons.DPad.Down == ButtonState.Pressed)
                    _AccurateY += vel;
                if (currentButtons.DPad.Left == ButtonState.Pressed)
                    _AccurateX -= vel;
                if (currentButtons.DPad.Right == ButtonState.Pressed)
                    _AccurateX += vel;
            }
            if (IsAnalogGamepadMovementEnabled)
            {
                // analog
                _AccurateX += vel * currentButtons.ThumbSticks.Left.X;
                _AccurateY -= vel * currentButtons.ThumbSticks.Left.Y;
            }
            #endregion

            // ensure that the cursor is inside the window
            if (_AccurateX < 0)
                _AccurateX = 0;
            else if (_AccurateX > _GraphicsDevice.PresentationParameters.BackBufferWidth)
                _AccurateX = _GraphicsDevice.PresentationParameters.BackBufferWidth - 1;

            if (_AccurateY < 0)
                _AccurateY = 0;
            else if (_AccurateY > _GraphicsDevice.PresentationParameters.BackBufferHeight)
                _AccurateY = _GraphicsDevice.PresentationParameters.BackBufferHeight - 1;

            // assign new position values
            if ((int)_AccurateX != Position.X || (int)_AccurateY != Position.Y)
            {
                SetPosition((int)_AccurateX, (int)_AccurateY);
            }
        }

        public void SetPosition(int x, int y)
        {
            Position = new Vector2(x, y);
#if WINDOWS
            Mouse.SetPosition(x, y);
#endif
        }

        public bool IsMouseInWindow(MouseState mouse)
        {
#if WINDOWS
            return mouse.X >= 0
                && mouse.Y >= 0
                && mouse.X < _GraphicsDevice.PresentationParameters.BackBufferWidth
                && mouse.Y < _GraphicsDevice.PresentationParameters.BackBufferHeight;
#else
            return true;
#endif
        }

        private void OnButtonPressed(CursorButton button)
        {
            if (ButtonPressed != null)
                ButtonPressed(button);
        }
        private void OnButtonReleased(CursorButton button)
        {
            if (ButtonReleased != null)
                ButtonReleased(button);
        }

        #region IDisposable Member

        public void Dispose()
        {
            this.ButtonPressed = null;
            this.ButtonReleased = null;
            this._GraphicsDevice = null;
        }

        #endregion
    }
}